package com.glodon.paas.consumer.service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.signature.QueryStringSigningStrategy;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.glodon.paas.consumer.service.bean.APIInfoBase;
import com.glodon.paas.consumer.service.bean.AccountAPIInfo;
import com.glodon.paas.consumer.service.bean.ActivityAPIInfo;
import com.glodon.paas.consumer.service.bean.DocumentAPIInfo;
import com.glodon.paas.consumer.service.bean.FileAPIInfo;
import com.glodon.paas.consumer.service.bean.QtoAPIInfo;
import com.glodon.paas.consumer.service.bean.RecyclerAPIInfo;
import com.glodon.paas.consumer.service.bean.ShareAPIInfo;
import com.glodon.paas.security.oauth1.OAuthConsumerEx;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

/**
 * @author: Don Li
 */
@Controller
@RequestMapping("/api")
public class RestAPITestController {

    private String  body;

    private String  methodType;

    private String  path;

    private String  apitype;
    private String  apisystem;

    OAuthProvider   provider;
    OAuthConsumer   consumer;

    OAuthConsumerEx myConsumer;

    @RequestMapping(value = "/system", method = RequestMethod.GET)
    public ModelAndView apitype(HttpServletRequest request, HttpServletResponse response, @FormParam("apitype")
    String apitype, @FormParam("apisystem")
    String apisystem, ModelMap model) throws ServletException, IOException {

        APIInfoBase apiinfo;

        if ("account".equals(apisystem)) {
            apiinfo = new AccountAPIInfo(apitype);
        } else if ("document".equals(apisystem)) {
            apiinfo = new DocumentAPIInfo(apitype);
        } else if ("file".equals(apisystem)) {
            apiinfo = new FileAPIInfo(apitype);
        } else if ("recycler".equals(apisystem)) {
            apiinfo = new RecyclerAPIInfo(apitype);
        } else if ("share".equals(apisystem)) {
            apiinfo = new ShareAPIInfo(apitype);
        } else if ("QTO".equals(apisystem)) {
            apiinfo = new QtoAPIInfo(apitype);
        } else if ("activity".equals(apisystem)) {
            apiinfo = new ActivityAPIInfo(apitype);
        } else {
            apiinfo = new AccountAPIInfo(apitype);
        }
        apiinfo.setBaseURI("http://" + request.getServerName() + ":" + request.getServerPort() + apiinfo.getBaseURI());
        request.setAttribute("apisystem", apisystem);
        request.setAttribute("apitype", apitype);
        request.setAttribute("baseURI", apiinfo.getBaseURI());
        request.setAttribute("apiinfo", apiinfo.build());
        return new ModelAndView("APITest");
    }

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public ModelAndView test(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws Exception {
        provider = (OAuthProvider) request.getSession().getAttribute("provider");
        consumer = (OAuthConsumer) request.getSession().getAttribute("consumer");
        apitype = request.getParameter("apitype");

        String param = request.getParameter("path").trim();
        int index = param.lastIndexOf('?');
        if (index >= 0) {
            param = URLEncoder.encode(param.substring(0, index), "UTF-8") + param.substring(index);
        } else {
            param = URLEncoder.encode(param, "UTF-8");
        }
        path = request.getParameter("baseURI") + param.replaceAll("%2F", "/");
        methodType = request.getParameter("methodType");
        body = request.getParameter("body");

        if (!isInternal()) {
            myConsumer = (OAuthConsumerEx) consumer;
            myConsumer.setSigningStrategy(new QueryStringSigningStrategy());
        }

        StringBuffer data = new StringBuffer();
        if (StringUtils.isEmpty(methodType)) {
            methodType = "get";
        }

        if (methodType.equals("get")) {
            query(data);
        } else if (methodType.equals("delete")) {
            delete(data);
        } else if (methodType.equals("post")) {
            if (path.indexOf("fileSize") > 0) {
                createFile(data);
            } else {
                post(data);
            }
        } else if (methodType.equals("put")) {
            put(data);
        } else {
            query(data);
        }
        model.addAttribute("data", data.toString());
        return new ModelAndView("APITest", model);
    }

    private void query(StringBuffer data) throws Exception {
        String signedUrl = isInternal() ? path : myConsumer.sign(path);
        WebResource resource = Client.create().resource(signedUrl);
        resource.header("Content-Type", "application/json;charset=UTF-8");
        ClientResponse c = resource.type(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);
        if (c.getStatus() == 204) {
            data.append("empty");
        } else {
            data.append(c.getEntity(String.class));
        }
    }

    private void post(StringBuffer data) throws IOException, OAuthMessageSignerException,
                                        OAuthExpectationFailedException, OAuthCommunicationException {
        String signedUrl = isInternal() ? path : myConsumer.sign(path, "POST");
        WebResource resource = Client.create().resource(signedUrl);
        ClientResponse c = resource.type(MediaType.APPLICATION_OCTET_STREAM).post(ClientResponse.class, body);
        if (c.getStatus() == 204) {
            data.append("empty");
        } else {
            data.append(c.getEntity(String.class));
        }
    }

    private void put(StringBuffer data) throws IOException, OAuthMessageSignerException,
                                       OAuthExpectationFailedException, OAuthCommunicationException {
        String signedUrl = isInternal() ? path : myConsumer.sign(path, "PUT");
        WebResource resource = Client.create().resource(signedUrl);
        ClientResponse c = resource.type(MediaType.APPLICATION_JSON_TYPE).put(ClientResponse.class, body);
        if (c.getStatus() == 204) {
            data.append("empty");
        } else {
            data.append(c.getEntity(String.class));
        }
    }

    private void delete(StringBuffer data) throws IOException, OAuthMessageSignerException,
                                          OAuthExpectationFailedException, OAuthCommunicationException {
        String signedUrl = isInternal() ? path : myConsumer.sign(path, "DELETE");
        WebResource resource = Client.create().resource(signedUrl);
        ClientResponse c = resource.type(MediaType.APPLICATION_JSON_TYPE).delete(ClientResponse.class);
        if (c.getStatus() == 204) {
            data.append("empty");
        } else {
            data.append(c.getEntity(String.class));
        }
    }

    private void createFile(StringBuffer data) throws OAuthMessageSignerException, OAuthExpectationFailedException,
                                              OAuthCommunicationException, IOException {
//        InputStream is = new ByteArrayInputStream("Data of Sample File".getBytes());
        InputStream is = new FileInputStream(new File("C:\\Users\\Public\\Videos\\Sample Videos\\test.wmv"));

        URI pathURI = UriBuilder.fromUri(path.replace("{fileSize}", String.valueOf(is.available()))).build();
        String signedUrl = isInternal() ? pathURI.toString() : myConsumer.sign(pathURI.toString(), "POST");
        WebResource resource = Client.create().resource(signedUrl);

        ClientResponse c = resource.type(MediaType.APPLICATION_OCTET_STREAM).post(ClientResponse.class, is);
        if (c.getStatus() == 204) {
            data.append("empty");
        } else {
            data.append(c.getEntity(String.class));
        }
    }

    private boolean isInternal() {
        return "internal".equalsIgnoreCase(apitype);
    }
}
